package com.itita.ww2.model.worldmap;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.commons.lang.StringUtils;
import org.joda.time.DateTime;

import com.itita.ww2.core.WW2Exception;
import com.itita.ww2.data.WorldMapConfig;
import com.itita.ww2.enums.WW2ExceptionCode;
import com.itita.ww2.game.user.UserService;
import com.itita.ww2.game.worldmap.WorldMapManager;
import com.itita.ww2.model.user.UserProfile;
import com.itita.ww2.model.worldmap.enums.CityType;
import com.itita.ww2.model.worldmap.enums.StatusEnum;
import com.itita.ww2.redis.RedisUtil;
import com.itita.ww2.redis.cache.Table;
import com.itita.ww2.utils.DataUtil;
import com.itita.ww2.utils.TimeUtil;

/**
 * 城市基类
 * @author NorthLan
 *
 */
public class City {
	private int blockId;
	private int cityId;
	private Block parentBlock;
	// 
	private ConcurrentHashMap<Integer, Base> baseMap; // 据点列表
	//
	private Table table;
	private StatusEnum status = StatusEnum.DEFAULT;
	// 
	private CityType cityType;
	private String declarAid = "";
	private String ownerAid = "";
	private DateTime lastOccTime;
	private DateTime lastAtkTime;
	private DateTime lastDeclarTime;
	/**
	 * 此构造方法构造出的对象<br>
	 * 不可使用其功能
	 */
	public City(int blockId, int cityId) {
		this.cityId = cityId;
		this.blockId = blockId;
	}

	public City(int cityId, Block parentBlock) {
		this.cityId = cityId;
		this.blockId = parentBlock.getId();
		this.parentBlock = parentBlock;
		this.baseMap = new ConcurrentHashMap<>();
		this.table = WorldMapManager.getInstance().getCacheCityTable();
		initData();
	}

	private void initData() {
		cityType = CityType.intValueOf(DataUtil.intValueOf(table.getValue(RedisUtil.genericKey(blockId, cityId), "type")));
		declarAid = table.getValue(RedisUtil.genericKey(blockId, cityId), "declarAid");
		ownerAid = table.getValue(RedisUtil.genericKey(blockId, cityId), "ownerAid");
		lastOccTime = DataUtil.timeValueOf(table.getValue(RedisUtil.genericKey(blockId, cityId), "lastOccTime"));
		lastAtkTime = DataUtil.timeValueOf(table.getValue(RedisUtil.genericKey(blockId, cityId), "lastAtkTime"));
		lastDeclarTime = DataUtil.timeValueOf(table.getValue(RedisUtil.genericKey(blockId, cityId), "lastDeclarTime"));
	}

	public void addBase(int baseId) {
		if (baseMap.containsKey(baseId)) {
			return;
		}
		baseMap.put(baseId, new Base(baseId, this));
	}

	public Base getBase(int baseId) {
		return baseMap.get(baseId);
	}

	public Collection<Base> getAllBase() {
		return this.baseMap.values();
	}

	public boolean isBaseExists(int baseId) {
		return baseMap.containsKey(baseId);
	}

	/**
	 * 城池被进攻
	 */
	private void attacked() {
		setLastAtkTime(TimeUtil.getUtcNow());
	}

	/**
	 * 联盟占领城池
	 */
	private void occupied(String aid) {
		setLastOccTime(TimeUtil.getUtcNow());
		setOwnerAid(aid);
		setDeclarAid("");
	}

	/**
	 * 刷新状态<br>
	 * 被同一个联盟占下 -> ownerAid <br>
	 * 有其中一个据点被非原占领联盟占领,表示城池被攻击
	 * @return 是否易主
	 */
	public boolean refreshOwnerState() {
		String preOwnerAid = getOwnerAid();
		Collection<Base> baseList = getAllBase();
		int size = baseList.size();
		Map<String, Integer> countMap = new HashMap<>(); // 非原联盟占领据点数量  aid : count
		for (Base base : baseList) {
			int ownerUid = base.getOwnerUid();
			// 没有被占领
			if (ownerUid == -1) {
				continue;
			}
			UserProfile ownerProfile = UserService.getInstance().getUserProfile(ownerUid);
			if (ownerProfile == null) {
				// null,可能出现问题
				continue;
			}
			String ownerAid = ownerProfile.getAid();
			if (StringUtils.isBlank(ownerAid)) {
				continue;
			}
			// 原占领联盟不计
			//			if (ownerAid.equalsIgnoreCase(preOwnerAid)) {
			//				continue;
			//			}
			if (!countMap.containsKey(ownerAid)) {
				countMap.put(ownerAid, 1);
			} else {
				countMap.put(ownerAid, countMap.get(ownerAid) + 1);
			}
		}
		boolean ret = false;

		// 所有据点被同一个联盟占领
		for (Entry<String, Integer> entry : countMap.entrySet()) {
			String aid = entry.getKey();
			if (entry.getValue() == size) {
				// 是否易主
				if (!aid.equalsIgnoreCase(preOwnerAid)) {
					ret = true;
					occupied(aid);
				}
				break;
			}
		}

		if (countMap.size() <= 0) {
			ret = true;
			reset();
		}

		if (!ret && countMap.size() > 0) {
			attacked();
		}
		return ret;
	}

	/**
	 * 当前城池状态
	 */
	public StatusEnum getStatus() {
		return this.status;
	}

	public void refreshStatus() {
		String ownerAid = getOwnerAid();
		if (isUnderAtk(ownerAid)) {
			status = StatusEnum.UNDER_ATTACK;
		} else {
			status = StatusEnum.DEFAULT;
		}
	}

	public boolean isOccupied() {
		return StringUtils.isNotBlank(getOwnerAid());
	}

	public void checkDeclarable(String aid) throws WW2Exception {
		// 城池等级限定
		if (!WorldMapConfig.isCityCanDeclarByType(getType().getValue())) {
			throw new WW2Exception(WW2ExceptionCode.LEVEL_LIMIT);
		}
		// 城池占用情况
		String ownerAid = getOwnerAid();
		if (StringUtils.isBlank(ownerAid) || aid.equals(ownerAid)) {
			throw new WW2Exception(WW2ExceptionCode.WORLDMAP_CITY_DECLAR_NO_NEED);
		}

		String declarAid = getDeclarAid();
		if (StringUtils.isNotBlank(declarAid)) {
			if (declarAid.equals(aid)) {
				throw new WW2Exception(WW2ExceptionCode.DUPLICATE_OPT);
			} else {
				throw new WW2Exception(WW2ExceptionCode.WORLDMAP_CITY_HAS_BEEN_DECLARED);
			}
		}
	}

	private boolean isUnderAtk(String ownerAid) {
		// 其中一个据点ownerAid与当前城市ownerAid不同,代表正在被攻击
		// 如果据点没有被占领,不为匹配项
		return getAllBase().stream().anyMatch(p -> {
			int ownerUid = p.getOwnerUid();
			if (ownerUid == -1) {
				return false;
			}
			UserProfile profile = UserService.getInstance().getUserProfile(p.getOwnerUid());
			if (profile == null) {
				return false;
			}
			String aid = profile.getAid();
			if (StringUtils.isBlank(aid)) {
				return false;
			}
			return !aid.equals(ownerAid);
		});
	}

	/**
	 * 重置所有状态
	 */
	public void reset() {
		setOwnerAid("");
		setDeclarAid("");
		setLastAtkTime(null);
		setLastDeclarTime(null);
		setLastOccTime(null);
	}

	/////////////////////////////////////////////////////

	public CityType getType() {
		return this.cityType;
	}

	public String getDeclarAid() {
		return this.declarAid;
	}

	public void setDeclarAid(String aid) {
		this.declarAid = aid;
		table.setValue(RedisUtil.genericKey(blockId, cityId), "declarAid", aid);
	}

	public String getOwnerAid() {
		return this.ownerAid;
	}

	public void setOwnerAid(String aid) {
		this.ownerAid = aid;
		table.setValue(RedisUtil.genericKey(blockId, cityId), "ownerAid", aid);
	}

	public DateTime getLastOccTime() {
		return this.lastOccTime;
	}

	public void setLastOccTime(DateTime time) {
		this.lastOccTime = time;
		String ret = "";
		if (time != null) {
			ret = time.toString(TimeUtil.redisTimestampFormatter);
		}
		table.setValue(RedisUtil.genericKey(blockId, cityId), "lastOccTime", ret);
	}

	public DateTime getLastAtkTime() {
		return this.lastAtkTime;
	}

	public void setLastAtkTime(DateTime time) {
		this.lastAtkTime = time;
		String ret = "";
		if (time != null) {
			ret = time.toString(TimeUtil.redisTimestampFormatter);
		}
		table.setValue(RedisUtil.genericKey(blockId, cityId), "lastAtkTime", ret);
	}

	public DateTime getLastDeclarTime() {
		return this.lastDeclarTime;
	}

	public void setLastDeclarTime(DateTime time) {
		this.lastDeclarTime = time;
		String ret = "";
		if (time != null) {
			ret = time.toString(TimeUtil.redisTimestampFormatter);
		}
		table.setValue(RedisUtil.genericKey(blockId, cityId), "lastDeclarTime", ret);
	}

	////////////////////////////////////////
	public int getId() {
		return cityId;
	}

	public Block getParentBlock() {
		return parentBlock;
	}

	public final int getBlockId() {
		return blockId;
	}
}
